// 侯捷老师C++程序设计课程中的template模板相关的代码测试

using namespace std;

// ------------------------------------------------------------------
// 类模板 Class Templates

#include <iostream>
namespace mcl01
{
// 复数类模板定义
template <typename T>
class complex
{
public:
    complex(T r = 0, T i = 0)
     : re(r), im(i)
    { }
    complex& operator += (const complex& _c)
    {
        (*this).re += _c.re;
        (*this).im += _c.im;
        return *this;
    }
    T real() const { return re; }
    T imag() const { return im; }
private:
    T re, im;

    friend complex<T>& __doapl(complex<T>* ths, const complex<T>& rhs);
};

template <typename T>
complex<T>& __doapl(complex<T>* ths, const complex<T>& rhs)
{
    ths->re += rhs.re;
    ths->im += rhs.im;
    return *ths;
}

void test_class_templates()
{
    cout << "test_class_templates().........." << endl;

complex<double> c1(2.5, 1.5);
complex<int> c2(2, 6);
complex<int> c3(4, 12);
    c2 += c3;
    cout << "complex.real()=" << c2.real() << endl;
    cout << "complex.imag()=" << c2.imag() << endl;
}
} // namespace mcl01


// -----------------------------------------------------------------
// 函数模板 Function Templates

#include <iostream>
namespace mcl02
{
class stone
{
public:
    stone() { }
    stone(int w, int h, int we)
     : _w(w), _h(h), _weight(we)
        { }
    bool operator<(const stone& rhs) const
        { return _weight < rhs._weight; }

    int w() const { return _w; }
    int h() const { return _h; }
    int weight() const { return _weight; }
    
private:
    int _w, _h, _weight;
};

template <class T>
inline
const T& min(const T& a, const T& b)
{
    return b < a ? b : a;
}

void test_function_templates()
{
    cout << "test_function_templates().........." << endl;

stone r1(2,3, 13), r2(3,3, 10), r3;
    // 编译器对函数模板进行实参推导（argument deduction）
    // 实参推导的结果：T为stone，于是调用stone::operator<() 
    r3 = min(r1, r2);
    cout << "r3.w= " << r3.w() << ", h= " << r3.h() << ", weight= " << r3.weight() << endl;
}
} // namespace mcl02



// -----------------------------------------------------------------------
// 智能指针 pointer-like classes

#include <iostream>
namespace macl03
{
template<class T>
class shared_ptr
{
public:
    T& operator*() const
    { 
        cout << "shared_ptr::operator*()" << endl;
        return *px; 
    }

    T* operator->() const
    { 
        cout << "shared_ptr::operator->()" << endl;
        return px; 
    }

    shared_ptr(T* p) : px(p) { cout << "shared_ptr ctor" << endl; }

private:
    T*      px;
    long*   pn;
};

struct Foo
{
public:
    void method(void) { cout << "Foo::method()" << endl; }
};

void test_shared_ptr()
{
    cout << "test_shared_ptr().........." << endl;

    shared_ptr<Foo> sp(new Foo); // shared_ptr ctor

    Foo f(*sp); // operator*
    sp->method(); // operator->, then call method()

} // test_shared_ptr
} // namespace macl03



// ----------------------------------------------------------------
// Member Templates，成员模板

#include <memory> // shared_ptr
#include <iostream>
namespace macl04
{
template <class T1, class T2>
struct pair {
    typedef T1 first_type;
    typedef T2 second_type;

    T1 first;
    T2 second;

    pair()
    : first(T1()), second(T2()) {}
    pair(const T1& a, const T2& b)
    : first(a), second(b) {}

    // 成员模板
    template <class U1, class U2>
    pair(const pair<U1, U2>& p)
    : first(p.first), second(p.second) {}
};




class Base1 {}; // 假设：鱼类
class Derived1 : public Base1 {}; // 假设：鲫鱼

class Base2 {}; // 假设：鸟类
class Derived2 : public Base2 {}; // 假设：麻雀

void test_member_templates()
{
    cout << "test_member_templates().........." << endl;

    pair<Derived1, Derived2> p; // 鲫鱼和麻雀组成的pair
    pair<Base1, Base2> p2(p); // 放进由鱼类和鸟类组成的pair
    //pair<Base1, Base2> p3(pair<Derived1, Derived2>()); // 未调用原型函数(是否是有意用变量定义的?) 

    Base1* ptr = new Derived1; // up-cast
    shared_ptr<Base1> sptr(new Derived1); // 模拟up-cast  
        // shared_ptr这里调用的构造函数也是成员模板函数
}

} // namespace macl04




// -----------------------------------------------------------
// 模板特化

#include <iostream>
namespace macl05
{
// 泛化版本
template <class Key>
struct hash { };

// 特化版本
template<>
struct hash<char> {
    size_t operator()(char x) const { return x; }
};

template<>
struct hash<int> {
    size_t operator()(int x) const { return x; }
};

template<>
struct hash<long> {
    size_t operator()(long x) const { return x; }
};

void test_specialization()
{
    cout << "test_specialization().........." << endl;

    cout << hash<char>()('A') << endl;
    cout << hash<int>()(1000) << endl;
    cout << hash<long>()(100000000) << endl;
}

} // namespace macl05



// ------------------------------------------------------------------
// 模板偏特化
// 1. 个数的偏特化
// 2. 范围的偏特化

#include <iostream>
namespace macl06
{

// 个数的偏特化
template <typename T, typename P>
class C 
{ 
public:
    C(T type = T(), P value = P())
    :_type(type), _value(value) { cout << "C<T, P> ctor" << endl; }

private:
    T _type;
    P _value;
};

template <typename P>
class C<bool, P>
{
public:
    C(P value = P())
    :_type(false), _value(value) { cout << "C<bool, P> ctor" << endl; }

private:
    bool _type;
    P _value;
};


// 范围的偏特化

template <typename T>
class CC
{
public:
    CC(const T& val_ = T())
    : val(val_) {}
private:
    T val;
};

template <typename T>
class CC<T*>
{
public:
    CC(T* val_ = nullptr)
    : pval(val_) {}
private:
    T* pval;
};

void test_partial_specialization()
{
    cout << "test_partial_specialization().........." << endl;

    C<int, string> c1(20, "hello");
    C<bool, int> c2(100);

    CC<string> cc1; // 调用泛化版本
    CC<string*> cc2; // 调用偏特化的版本
}

} // namespace macl06



// ----------------------------------------------------------------
// 模板模板参数 template template parameter

#include <list>
#include <memory>
#include <deque>
namespace macl07
{
template <typename T, 
          template <typename T>
            class Container
         >
class XCls
{
public:

private:
    Container<T> c;
};

template<typename T>
using Lst = list<T, allocator<T>>;



template <typename T,
          template <typename T>
            class SmartPtr
         >
class XCls2
{
private:
    SmartPtr<T> sp;
public:
    XCls2() : sp(new T) { }
};


// 这不是template template parameter
template <class T, class Sequence = deque<T>>
class stack
{
    friend bool operator==(const stack&, const stack&);
    friend bool operator<(const stack&, const stack&);

protected:
    Sequence c; // 底层容器
};

void test_template_template_parameter()
{
    cout << "test_template_template_parameter().........." << endl;

    //! XCls<string, list> mylst1; // 类模板“std::list”的模板参数列表与模板参数“Container”的模板参数列表不匹配
    XCls<string, Lst> mylst2; // ok。第二个模板参数是类模板参数


    XCls2<string, shared_ptr> p1;
    //XCls2<double, unique_ptr> p2; // 类模板“std::unique_ptr”的模板参数列表与模板参数“SmartPtr”的模板参数列表不匹配
    //XCls2<int, weak_ptr> p3; // “std::weak_ptr<T>::weak_ptr”: 3 个重载中没有一个可以转换所有参数类型
    //XCls2<long, auto_ptr> p4;

    // 这不是template template parameter
    stack<int> s1;
    stack<int, list<int>> s2; // 因为list<int> 已经是特化的，不是类模板参数
}
} // namespace macl07



// --------------------------------------------------------------------------------
// variadic templates (since c++11) 数量不定的模板参数

// ...就是一个所谓的pack（包）
// 用于template parameters，就是template parameters pack（模板参数包）
// 用于function parameters types，就是function parameters types pack（函数参数类型包）
// 用于function parameters，就是function parameters pack（函数参数包）

// 在variadic templates内部，sizeof...(args) 指示参数的个数

#include <bitset>
namespace macl08
{
void print()
{
}

template <typename T, typename... Types>
void print(const T& firstArg, const Types&... args)
{
    cout << firstArg << endl;
    print(args...); // 递归调用，不停的拆包，直到参数个数为空，调用print()结束
}

void test_variadic_templates()
{
    cout << "test_variadic_templates().........." << endl;

    print(7.5, "hello", bitset<16>(377), 42);
}
} // namespace macl08



// -------------------------------------------------------------
// 一个万用的hash function（variadic template的一个使用）
// cppvsdbg

#include <functional>
#include <unordered_set>
#include <string>
namespace macl09
{

// from boost (functional/hash)
template <typename T>
inline void hash_combine(size_t& seed, const T& val)
{
    seed ^= std::hash<T>()(val) + 0x9e3779b9
        + (seed<<6) + (seed>>2);
}

// auxiliary generic functions to create a hash value using a seed
template <typename T>
inline void hash_val(size_t& seed, const T& val)
{
    hash_combine(seed, val);
}

template <typename T, typename... Types>
inline void hash_val(size_t& seed, const T& val, const Types&... args)
{
    hash_combine(seed, val);
    hash_val(seed, args...);
}

// auxiliary generic function
template <typename... Types>
inline size_t hash_val(const Types&... args)
{
    size_t seed = 0;
    hash_val(seed, args...);
    return seed;
}

class Customer
{
public:
    Customer(const string fname_, const string lname_, const int no_)
    : fname(fname_), lname(lname_), no(no_) 
    { }
public:
    string fname;
    string lname;
    int no;
};


/*
template<class _Kty,
	class _Hasher = hash<_Kty>,
	class _Keyeq = equal_to<_Kty>,
	class _Alloc = allocator<_Kty>>
	class unordered_set

template<class _Ty = void>
	struct equal_to
	{	// functor for operator==
	_CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty first_argument_type;
	_CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty second_argument_type;
	_CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef bool result_type;

	constexpr bool operator()(const _Ty& _Left, const _Ty& _Right) const
		{	// apply operator== to operands
		return (_Left == _Right);
		}
	};     
*/
// unordered_set的equal_to中会判断_Left == _Right，
// 所以需要重载operator == 支持Customer的比较
inline bool operator == (const Customer& a, const Customer& b)
{
    return (a.fname == b.fname && a.lname == b.lname && a.no == b.no);
}

class CustomerHash
{
public:
    size_t operator()(const Customer& c) const {
        return hash_val(c.fname, c.lname, c.no);
    }
};

void test_hush_function()
{
    cout << "test_hush_function().........." << endl;

    unordered_set<Customer, CustomerHash> set;
    set.insert(Customer("Ace",   "Hou",  1L));
    set.insert(Customer("Sabri",   "Hou",  2L));
    set.insert(Customer("Stacy",   "Chen",  3L));
    set.insert(Customer("Mike",   "Tseng",  4L));
    set.insert(Customer("Paili",   "Chen",  5L));
    set.insert(Customer("Light",   "Shiau",  6L));
    set.insert(Customer("Shally",   "Yang",  7L));
    cout << "set current bucket_count= " << set.bucket_count() << endl; // 8


    CustomerHash hh;
    cout << "bucket position of Ace = " << hh(Customer("Ace",   "Hou",  1L)) % 11 << endl;  // 2
    cout << "bucket position of Sabri = " << hh(Customer("Sabri",   "Hou",  2L)) % 11 << endl;  // 5
    cout << "bucket position of Stacy = " << hh(Customer("Stacy",   "Chen",  3L)) % 11 << endl; // 0
    cout << "bucket position of Mike = " << hh(Customer("Mike",   "Tseng",  4L)) % 11 << endl;  // 10
    cout << "bucket position of Paili = " << hh(Customer("Paili",   "Chen",  5L)) % 11 << endl; // 10
    cout << "bucket position of Light = " << hh(Customer("Light",   "Shiau",  6L)) % 11 << endl;    // 6
    cout << "bucket position of Shally = " << hh(Customer("Shally",   "Yang",  7L)) % 11 << endl;   // 6

    for(unsigned i=0; i<set.bucket_count(); ++i) {
        cout << "bucket #" << i << " has " << set.bucket_size(i) << " elements." << endl; // 
    }
    // bucket #0 has 1 elements.
    // bucket #1 has 1 elements.
    // bucket #2 has 1 elements.
    // bucket #3 has 2 elements.
    // bucket #4 has 2 elements.
    // bucket #5 has 0 elements.
    // bucket #6 has 0 elements.
    // bucket #7 has 0 elements.
}
} // namespace macl09


int main()
{
    mcl01::test_class_templates();
    mcl02::test_function_templates();
    macl03::test_shared_ptr();
    macl04::test_member_templates();
    macl05::test_specialization();
    macl06::test_partial_specialization();
    macl07::test_template_template_parameter();
    macl08::test_variadic_templates();
    macl09::test_hush_function();
}